/*
 * Copyright (c) 2011, Julien Lamy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "itunesdatabase.h"

#include <iterator>
#include <set>
#include <stdint.h>

#include "album.h"
#include "artist.h"
#include "chunkreader.h"
#include "logging.h"
#include "track.h"

struct AlbumComparator
{
    bool operator()(Album const & a1, Album const & a2) const
    {
        return ((a1[DataObjectType::ALBUM_LIST_ALBUM]<a2[DataObjectType::ALBUM_LIST_ALBUM]) ||
                (a2[DataObjectType::ALBUM_LIST_ALBUM]==a2[DataObjectType::ALBUM_LIST_ALBUM] &&
                 a1[DataObjectType::ALBUM_LIST_ARTIST]<a2[DataObjectType::ALBUM_LIST_ARTIST]));
    }
};

struct ArtistComparator
{
    bool operator()(Artist const & a1, Artist const & a2) const
    {
        return (a1[DataObjectType::ARTIST]<a2[DataObjectType::ARTIST]);
    }
};

void ITunesDataBase::update_albums()
{
    std::set<Album, AlbumComparator> albums_set;

    for(std::vector<Track>::const_iterator it=tracks.begin(); it!=tracks.end(); ++it)
    {
        Album album;
        album[DataObjectType::ALBUM_LIST_ALBUM] = (*it)[DataObjectType::ALBUM_LIST_ALBUM];
        album[DataObjectType::ALBUM_LIST_ARTIST] = (*it)[DataObjectType::ALBUM_LIST_ARTIST];

        if((*it)[DataObjectType::ALBUM_LIST_SORT_ARTIST] != "")
        {
            album[DataObjectType::ALBUM_LIST_SORT_ARTIST] = (*it)[DataObjectType::ALBUM_LIST_SORT_ARTIST];
        }
        if((*it)[DataObjectType::ALBUM_LIST_PODCAST_URL] != "")
        {
            album[DataObjectType::ALBUM_LIST_PODCAST_URL] = (*it)[DataObjectType::ALBUM_LIST_PODCAST_URL];
        }
        if((*it)[DataObjectType::ALBUM_LIST_SHOW_NAME] != "")
        {
            album[DataObjectType::ALBUM_LIST_SHOW_NAME] = (*it)[DataObjectType::ALBUM_LIST_SHOW_NAME];
        }

        albums_set.insert(album);
    }

    this->albums.clear();
    this->albums.reserve(albums_set.size());
    std::copy(albums_set.begin(), albums_set.end(), std::back_inserter(this->albums));
}

void ITunesDataBase::update_artists()
{
    std::set<Artist, ArtistComparator> artists_set;

    for(std::vector<Track>::const_iterator it=tracks.begin(); it!=tracks.end(); ++it)
    {
        Artist artist;
        artist[DataObjectType::ARTIST] = (*it)[DataObjectType::ARTIST];

        artists_set.insert(artist);
    }

    this->artists.clear();
    this->artists.reserve(artists_set.size());
    std::copy(artists_set.begin(), artists_set.end(), std::back_inserter(this->artists));
}

std::istream & operator>>(std::istream & stream, ITunesDataBase & itunes_database)
{
    ChunkReader reader(stream, "mhbd");

    if(!reader.done()) reader.read(itunes_database.unknown_1);
    if(!reader.done()) reader.read(itunes_database.version_number);
    if(!reader.done()) reader.read(itunes_database.number_of_children);
    if(!reader.done()) reader.read(itunes_database.id);
    if(!reader.done()) reader.read(itunes_database.unknown_2);
    if(!reader.done()) reader.read(itunes_database.unknown_3);
    if(!reader.done()) reader.read(itunes_database.unknown_4);
    if(!reader.done()) reader.read(itunes_database.hashing_scheme);
    if(!reader.done()) reader.read(itunes_database.unknown_5, 20);
    if(!reader.done()) reader.read(itunes_database.language, 2);
    itunes_database.language[2] = 0;
    if(!reader.done()) reader.read(itunes_database.library_persistent_id);
    if(!reader.done()) reader.read(itunes_database.unknown_6);
    if(!reader.done()) reader.read(itunes_database.unknown_7);
    if(!reader.done()) reader.read(itunes_database.obscure_hash, 20);
    if(!reader.done()) reader.read(itunes_database.time_zone);
    if(!reader.done()) reader.read(itunes_database.unknown_8);
    if(!reader.done()) reader.read(itunes_database.iphone_hash, 46);

    reader.ignore_rest_of_header();

    for(uint32_t i=0; i < itunes_database.number_of_children; ++i)
    {
        ChunkReader dataset_reader(stream, "mhsd");
        uint32_t dataset_type;
        dataset_reader.read(dataset_type);
        dataset_reader.ignore_rest_of_header();

        if(dataset_type == DataSetType::TRACK_LIST)
        {
            ChunkReader list_reader(stream, "mhlt");
            list_reader.ignore_rest_of_header();
            itunes_database.tracks.reserve(list_reader.total_length());
            for(uint32_t element_index=0; element_index<list_reader.total_length(); ++element_index)
            {
                Track track;
                stream >> track;
                itunes_database.tracks.push_back(track);
            }
        }
        else if(dataset_type == DataSetType::ALBUM_LIST)
        {
            ChunkReader list_reader(stream, "mhla");
            list_reader.ignore_rest_of_header();
            itunes_database.albums.reserve(list_reader.total_length());
            for(uint32_t element_index=0; element_index<list_reader.total_length(); ++element_index)
            {
                Album album;
                stream >> album;
                itunes_database.albums.push_back(album);
            }
        }
        else if(dataset_type == DataSetType::ARTIST_LIST)
        {
            ChunkReader list_reader(stream, "mhia");
            list_reader.ignore_rest_of_header();
            itunes_database.albums.reserve(list_reader.total_length());
            for(uint32_t element_index=0; element_index<list_reader.total_length(); ++element_index)
            {
                Artist artist;
                stream >> artist;
                itunes_database.artists.push_back(artist);
            }
        }
        else
        {
            BABELPOD_LOG_VERBOSE(INFO) << "Skipping dataset of type " << dataset_type << std::endl;
            stream.ignore(dataset_reader.total_length()-dataset_reader.header_length());
        }
    }

    return stream;
}
